package asset;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

public class RegFunct {

    private static Map<String, Consumer<String>> str_To_Function = new HashMap<>();

    static {
        str_To_Function.put("add", RegFunct::add);
        str_To_Function.put("sub", RegFunct::sub);
        str_To_Function.put("and", RegFunct::and);
        str_To_Function.put("or", RegFunct::or);
        str_To_Function.put("nor", RegFunct::nor);
        str_To_Function.put("slt", RegFunct::slt);
        str_To_Function.put("sltu", RegFunct::sltu);
        str_To_Function.put("sll", RegFunct::sll);
        str_To_Function.put("srl", RegFunct::srl);
        str_To_Function.put("jr", RegFunct::jr);
        str_To_Function.put("mult", RegFunct::mult);
        str_To_Function.put("multu", RegFunct::multu);
        str_To_Function.put("div", RegFunct::div);
        str_To_Function.put("divu", RegFunct::divu);
        str_To_Function.put("mfhi", RegFunct::mfhi);
        str_To_Function.put("mflo", RegFunct::mflo);
        str_To_Function.put("mthi", RegFunct::mthi);
        str_To_Function.put("mtlo", RegFunct::mtlo);
        str_To_Function.put("addi", RegFunct::addi);
        str_To_Function.put("lw", RegFunct::lw);
        str_To_Function.put("sw", RegFunct::sw);
        str_To_Function.put("lh", RegFunct::lh);
        str_To_Function.put("sh", RegFunct::sh);
        str_To_Function.put("lb", RegFunct::lb);
        str_To_Function.put("sb", RegFunct::sb);
        str_To_Function.put("beq", RegFunct::beq);
        str_To_Function.put("bne", RegFunct::bne);
        str_To_Function.put("slti", RegFunct::slti);
        str_To_Function.put("sltiu", RegFunct::sltiu);
        str_To_Function.put("lui", RegFunct::lui);
        str_To_Function.put("ori", RegFunct::ori);
        str_To_Function.put("andi", RegFunct::andi);
        str_To_Function.put("j", RegFunct::j);
        str_To_Function.put("jal", RegFunct::jal);
    }

    public static void funct(String name,String source) {
        str_To_Function.get(name).accept(source);
    }

    private static void add(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            RegFile.write(rd, a + b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sub(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            RegFile.write(rd, a - b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void and(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            RegFile.write(rd, a & b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void or(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            RegFile.write(rd, a | b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void nor(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            RegFile.write(rd, a ^ b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void slt(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            RegFile.write(rd, a < b ? 1 : 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sltu(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rs = tempStrings[1];
            String rt = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            long la = Integer.toUnsignedLong(a);
            long lb = Integer.toUnsignedLong(b);
            RegFile.write(rd, la < lb ? 1 : 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sll(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rt = tempStrings[1];
            String shamet = tempStrings[2];
            int a = Define.zeroExtNum(shamet);
            int b = RegFile.read(rt);
            RegFile.write(rd, b << a);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void srl(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rd = tempStrings[0];
            String rt = tempStrings[1];
            String shamet = tempStrings[2];
            int a = Define.zeroExtNum(shamet);
            int b = RegFile.read(rt);
            RegFile.write(rd, b >>> a);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void jr(String source) {
        try {
            String rs = source.strip();
            int nextPc = RegFile.read(rs);
            // 延迟槽
            Excute.delaying();

            PC.next(nextPc);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void mult(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rs = tempStrings[0];
            String rt = tempStrings[1];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            long la = a;
            long lb = b;
            long temp = la * lb;
            int hi = (int) (temp >>> 32);
            int lo = (int) temp;
            RegFile.write("hi", hi,"lo", lo);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void multu(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rs = tempStrings[0];
            String rt = tempStrings[1];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            long la = Integer.toUnsignedLong(a);
            long lb = Integer.toUnsignedLong(b);
            long temp = la * lb;
            int hi = (int) (temp >>> 32);
            int lo = (int) temp;
            RegFile.write("hi", hi,"lo", lo);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void div(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rs = tempStrings[0];
            String rt = tempStrings[1];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            int hi = a % b;
            int lo = a / b;
            RegFile.write("hi", hi,"lo", lo);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void divu(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rs = tempStrings[0];
            String rt = tempStrings[1];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            long la = Integer.toUnsignedLong(a);
            long lb = Integer.toUnsignedLong(b);
            int hi = (int) (la % lb);
            int lo = (int) (la / lb);
            RegFile.write("hi", hi,"lo", lo);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void mfhi(String source) {
        try {
            String rd = source.strip();
            int hi = RegFile.read("hi");
            RegFile.write(rd, hi);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void mflo(String source) {
        try {
            String rd = source.strip();
            int lo = RegFile.read("lo");
            RegFile.write(rd, lo);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void mthi(String source) {
        try {
            String rs = source.strip();
            int hi = RegFile.read(rs);
            RegFile.write("hi", hi);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void mtlo(String source) {
        try {
            String rs = source.strip();
            int lo = RegFile.read(rs);
            RegFile.write("lo", lo);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void addi(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String rs = tempStrings[1];
            String imm = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm, 16);
            RegFile.write(rt, a + b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void slti(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String rs = tempStrings[1];
            String imm = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm, 16);
            RegFile.write(rt, a < b ? 1 : 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sltiu(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String rs = tempStrings[1];
            String imm = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm, 16);
            long la = Integer.toUnsignedLong(a);
            long lb = Integer.toUnsignedLong(b);
            RegFile.write(rt, la < lb ? 1 : 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void ori(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String rs = tempStrings[1];
            String imm = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.zeroExtNum(imm);
            RegFile.write(rt, a | b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void andi(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String rs = tempStrings[1];
            String imm = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.zeroExtNum(imm);
            RegFile.write(rt, a & b);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void lw(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            String rs = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm);
            int addr = a + b;
            int dmread = DM.read(addr);
            RegFile.write(rt, dmread);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sw(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            String rs = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm);
            int addr = a + b;
            int grfRead = RegFile.read(rt);
            DM.write(addr, grfRead);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void lh(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            String rs = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm);
            int addr = a + b;
            int dmread = DM.read(addr, (byte) 2);
            RegFile.write(rt, dmread);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sh(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            String rs = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm);
            int addr = a + b;
            int grfRead = RegFile.read(rt);
            DM.write(addr, grfRead, (byte) 2);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void lb(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            String rs = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm);
            int addr = a + b;
            int dmread = DM.read(addr, (byte) 1);
            RegFile.write(rt, dmread);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void sb(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            String rs = tempStrings[2];
            int a = RegFile.read(rs);
            int b = Define.sigExtNum(imm);
            int addr = a + b;
            int grfRead = RegFile.read(rt);
            DM.write(addr, grfRead, (byte) 1);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void beq(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rs = tempStrings[0];
            String rt = tempStrings[1];
            String offsetString = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            int offset = Define.sigExtNum(offsetString) << 2;
            int nextPc = PC.getValue() + 4 + offset;
            if (a == b) {
                Excute.delaying();
                PC.next(nextPc);
            } else {
                PC.next();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void bne(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rs = tempStrings[0];
            String rt = tempStrings[1];
            String offsetString = tempStrings[2];
            int a = RegFile.read(rs);
            int b = RegFile.read(rt);
            int offset = Define.sigExtNum(offsetString) << 2;
            int nextPc = PC.getValue() + 4 + offset;
            if (a != b) {
                Excute.delaying();
                PC.next(nextPc);
            } else {
                PC.next();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void lui(String source) {
        try {
            String[] tempStrings = RegOrder.disSplits(source);
            String rt = tempStrings[0];
            String imm = tempStrings[1];
            int imm16 = Define.zeroExtNum(imm) << 16;
            RegFile.write(rt, imm16);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void j(String source) {
        try {
            int instrIndex = Define.zeroExtNum(source.strip()) << 2;
            int nextPc = instrIndex & 0x3fffffff | (PC.getValue() & 0xc0000000);
            Excute.delaying();
            PC.next(nextPc);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void jal(String source) {
        try {
            int instrIndex = Define.zeroExtNum(source.strip()) << 2;
            int nextPc = instrIndex & 0x3fffffff | (PC.getValue() & 0xc0000000);
            RegFile.write("$ra", PC.getValue() + 8);
            Excute.delaying(true);
            PC.next(nextPc);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
